﻿using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using LightCAD.MathLib;

namespace LightCAD.Core.Elements
{
    public class LcText : LcElement, ILcText
    {
        public Vector2 Start;
        //public Vector2 End;
        public double Heigh;
        public string Text;

        //文字起始点(左对齐的话 statr与TextStart 是一样的点位)
        //public Vector2 TextStart;

        //字体名称
        public string Typeface;
        //向量
        //public Vector2 XLpoint;
        //倾斜
        public double Tilt;
        //宽度因子
        public double Widthfactor;
        //旋转
        public double Rotate;

        //对齐方式
        public string Alignment;

        //通过文字大小  字体点 计算出来的boxsatrt点
        public Vector2 Boxstart;

        //public double Angle;
        public LcText()
        {
            this.Type = BuiltinElementType.LcText;
        }
        public LcText(Vector2 start, Vector2 end, double height, string text) : this()
        {
            this.Start = start;
            this.Heigh = height;
            this.Text = text;
        }
        [JsonIgnore]
        public double Angle
        {
            get
            {
                return (this.End - this.Start).Angle();
            }
        }

        public Vector2 TextStart
        {

            //向量根据起点，角度计算
            get
            {

                //if (this.Alignment == "居中")
                //{
                //    Vector2 lidisptpos = this.Start - (new Vector2(this.XLpoint.X * (this.Width / 2), this.XLpoint.Y * (this.Width / 2))) / Math.Sqrt(Math.Abs((Math.Pow(this.XLpoint.X, 2.0) + Math.Pow(this.XLpoint.Y, 2.0))));
                //    return lidisptpos;
                //}
                //else
                if (this.Alignment == "左对齐")
                {
                    return this.Start;

                }
                //else if (this.Alignment == "右对齐")
                //{
                //    Vector2 lidisptpos = this.Start - (new Vector2(this.XLpoint.X * this.Width, this.XLpoint.Y * this.Width)) / Math.Sqrt(Math.Abs((Math.Pow(this.XLpoint.X, 2.0) + Math.Pow(this.XLpoint.Y, 2.0))));
                //    return lidisptpos;
                //}
                else
                {
                    return this.Start;

                }

            }
            set {
               
            }
        }

        public Vector2 XLpoint
        {
            //向量根据起点，角度计算
            get
            {

                #region 如果输入的角度
                var newendpoint = Vector2.Rotate(new Vector2(this.TextStart.X + 100, this.TextStart.Y), this.TextStart, this.Rotate);
                #endregion
                //向量
                return new Vector2(newendpoint.X - this.TextStart.X, newendpoint.Y - this.TextStart.Y);
            }
        }

        public Vector2 End
        {
            //end 点是根据起点 +向量 +宽度
            get
            {
                //宽度公式
                //文字  英文字母  数字 特殊字符的长度不一样
                string chtext = "";
                string nochtext = "";
                string engtext = "";
                string tstext = "";

                Regex regex = new Regex(@"^[\u4E00-\u9FA5]{0,}$");

                for (int i = 0; i < this.Text.Length; i++)
                {
                    if (regex.IsMatch(this.Text[i].ToString()))
                    {
                        //中文
                        chtext = chtext + this.Text[i].ToString();
                    }
                    else
                    {
                        //不是中文
                        nochtext = nochtext + this.Text[i].ToString();
                    }
                }
                //取出字符串中所有的数字 ,字母      
                engtext = Regex.Replace(this.Text, @"[^A-Za-z0-9]+", "", RegexOptions.IgnoreCase);

                //处理特殊字符（特殊字符）
                tstext = Regex.Replace(nochtext, @"[A-Za-z0-9]", "", RegexOptions.IgnoreCase);

                //因为字母 数字 的长度只能给一半
                double dis = 0;
                //特殊字符按中文长度算
                if (tstext != "" && tstext.Length > 0)
                {
                    dis += this.Heigh * this.Widthfactor * tstext.Length * 1;
                }
                if (chtext != "" && chtext.Length > 0)
                {
                    dis += this.Heigh * this.Widthfactor * chtext.Length * 1;
                }
                if (engtext != "" && engtext.Length > 0)
                {
                    dis += Math.Round((this.Heigh * this.Widthfactor * engtext.Length * 1) / 2);
                }


                //double dis = this.Heigh * this.Widthfactor * this.Text.Length * 1;
                #region 如果输入的角度
                var newendpoint = Vector2.Rotate(new Vector2(this.TextStart.X + 100, this.TextStart.Y), this.TextStart, this.Rotate);
                Vector2 Comend = this.TextStart + (new Vector2(this.XLpoint.X * dis, this.XLpoint.Y * dis)) / Math.Sqrt(Math.Abs((Math.Pow(this.XLpoint.X, 2.0) + Math.Pow(this.XLpoint.Y, 2.0))));
                #endregion
                //向量
                return Comend;
            }
        }

        public double Width
        {
            get
            {
                //宽度公式 文字高度 *宽度因子*文字长度*1
                //double dis = this.Heigh * this.Widthfactor * this.Text.Length * 1;
                //宽度公式
                //文字  英文字母  数字 特殊字符的长度不一样
                string chtext = "";
                string nochtext = "";
                string engtext = "";
                string tstext = "";

                Regex regex = new Regex(@"^[\u4E00-\u9FA5]{0,}$");

                for (int i = 0; i < this.Text.Length; i++)
                {
                    if (regex.IsMatch(this.Text[i].ToString()))
                    {
                        //中文
                        chtext = chtext + this.Text[i].ToString();
                    }
                    else
                    {
                        //不是中文
                        nochtext = nochtext + this.Text[i].ToString();
                    }
                }
                //取出字符串中所有的数字 ,字母      
                engtext = Regex.Replace(this.Text, @"[^A-Za-z0-9]+", "", RegexOptions.IgnoreCase);

                //处理特殊字符（特殊字符）
                tstext = Regex.Replace(nochtext, @"[A-Za-z0-9]", "", RegexOptions.IgnoreCase);

                //因为字母 数字 的长度只能给一半
                double dis = 0;
                //特殊字符按中文长度算
                if (tstext != "" && tstext.Length > 0)
                {
                    dis += this.Heigh * this.Widthfactor * tstext.Length * 1;
                }

                if (chtext != "" && chtext.Length > 0)
                {
                    dis += this.Heigh * this.Widthfactor * chtext.Length * 1;
                }
                if (engtext != "" && engtext.Length > 0)
                {
                    dis += Math.Round((this.Heigh * this.Widthfactor * engtext.Length * 1) / 2);
                }
                return dis;
            }
        }

        public override LcElement Clone()
        {
            var clone = Document.CreateObject<LcText>();
            clone.Copy(this);
            return clone;
        }

        public void Set(Vector2? start = null, Vector2? textstart = null, string textvalue = "", string alignment = "", double? rotate = null, double? height = null, double? widthfactor = null, double? tilt = null, bool fireChangedEvent = true)
        {
            //PropertySetter:Center,Radius            
            if (!fireChangedEvent)
            {
                //if (textstart != null) this.TextStart = textstart;
                if (start != null) this.Start = start;
                //if (end != null) this.End = end;
            }
            else
            {
                bool chg_start = (start != null && start != this.TextStart);
                if (chg_start)
                {
                    OnPropertyChangedBefore(nameof(start), this.Start, start);
                    var oldValue = this.Start;
                    this.Start = start;
                    OnPropertyChangedAfter(nameof(start), oldValue, this.Start);
                }
                bool chg_textstart = (textstart != null && textstart != this.TextStart);
                if (chg_textstart)
                {
                    //OnPropertyChangedBefore(nameof(textstart), this.TextStart, textstart);
                    //var oldValue = this.TextStart;
                    //this.TextStart = textstart;
                    //OnPropertyChangedAfter(nameof(textstart), oldValue, this.TextStart);
                }
                //bool chg_end = (end != null && end != this.End);
                //if (chg_end)
                //{
                //    OnPropertyChangedBefore(nameof(end), this.End, end);
                //    var oldValue = this.End;
                //    this.End = end;
                //    OnPropertyChangedAfter(nameof(end), oldValue, this.End);
                //}
                bool chg_text = (textvalue != "" && textvalue != this.Text);
                if (chg_text)
                {
                    OnPropertyChangedBefore(nameof(textvalue), this.Text, textvalue);
                    var oldValue = this.Text;
                    this.Text = textvalue;
                    OnPropertyChangedAfter(nameof(textvalue), oldValue, this.Text);
                }
                bool chg_rotate = (rotate != null && rotate != this.Rotate);
                if (chg_rotate)
                {
                    OnPropertyChangedBefore(nameof(rotate), this.Rotate, rotate);
                    var oldValue = this.Rotate;
                    this.Rotate = Math.Round(rotate.Value);
                    OnPropertyChangedAfter(nameof(rotate), oldValue, this.Rotate);
                }

                bool chg_height = (height != null && height != this.Heigh);
                if (chg_height)
                {
                    OnPropertyChangedBefore(nameof(height), this.Heigh, height);
                    var oldValue = this.Heigh;
                    this.Heigh = Math.Round(height.Value);
                    OnPropertyChangedAfter(nameof(height), oldValue, this.Heigh);
                }

                bool chg_widthfactor = (widthfactor != null && widthfactor != this.Widthfactor);
                if (chg_widthfactor)
                {
                    OnPropertyChangedBefore(nameof(widthfactor), this.Widthfactor, widthfactor);
                    var oldValue = this.Widthfactor;
                    this.Widthfactor = Math.Round(widthfactor.Value);
                    OnPropertyChangedAfter(nameof(widthfactor), oldValue, this.Widthfactor);
                }
                bool chg_tilt = (tilt != null && tilt != this.Tilt);
                if (chg_tilt)
                {
                    OnPropertyChangedBefore(nameof(tilt), this.Tilt, tilt);
                    var oldValue = this.Tilt;
                    this.Tilt = Math.Round(tilt.Value);
                    OnPropertyChangedAfter(nameof(tilt), oldValue, this.Tilt);
                }

                bool chg_alignment = (alignment != "" && alignment != this.Alignment);
                if (chg_alignment)
                {
                    OnPropertyChangedBefore(nameof(alignment), this.Tilt, alignment);
                    var oldValue = this.Alignment;
                    this.Alignment = alignment;
                    OnPropertyChangedAfter(nameof(alignment), oldValue, this.Alignment);
                }
                ////重新计算盒子
                //GetBoundingBox();

            }
            ResetBoundingBox();
        }

        public override void Copy(LcElement src)
        {
        }

        //public override void Translate(Vector2 vector2)
        //{
        //}
        public override void Translate(double dx, double dy)
        {
            var tc = new Vector2(this.Start.X + dx, this.Start.Y + dy);
            Set(start: tc);
            ResetBoundingBox();
        }

        public LcLine GetLine()
        {

            List<Line2d> tt = GetShapeCurves(new Line2d(this.TextStart, this.End), this.Heigh);
            LcLine lcLine = new LcLine();
            if (this.Rotate > 0 && this.Rotate <= 180)
            {
                lcLine.Start = tt[1].Start;
                lcLine.End = tt[1].End;

            }
            else if ((this.Rotate > 180 && this.Rotate <= 360) || this.Rotate == 0)
            {
                lcLine.Start = tt[3].Start;
                lcLine.End = tt[3].End;

            }
            return lcLine;
        }
        public override Box2 GetBoundingBox()
        {

            //通过算法  0-180度之间 (不包含0) 取 数组[1]   180-360度之间 (包含360) 取 数组[3] 
            List<Line2d> tt = GetShapeCurves(new Line2d(this.TextStart, this.End), this.Heigh);
            Vector2[] listve = new Vector2[4];
            if (this.Rotate > 0 && this.Rotate <= 180)
            {
                listve[0] = tt[1].Start;
                listve[1] = tt[1].End;
                listve[2] = this.TextStart;
                listve[3] = this.End;
                this.Boxstart = tt[1].Start;

            }
            else if ((this.Rotate > 180 && this.Rotate <= 360) || this.Rotate == 0)
            {
                listve[0] = tt[3].Start;
                listve[1] = tt[3].End;
                listve[2] = this.TextStart;
                listve[3] = this.End;
                this.Boxstart = tt[3].Start;
            }

            //Vector2[] listve = new Vector2[] {
            //tt[3].Start,
            //tt[3].End,
            //this.TextStart,
            //this.End,
            //};
            return new Box2().SetFromPoints(listve);
        }



        public override Box2 GetBoundingBox(Matrix3 matrix)
        {
            return new Box2().SetFromPoints(matrix.MultiplyPoint(this.Boxstart), matrix.MultiplyPoint(this.End));
        }

        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                //如果元素盒子，与多边形盒子不相交，那就可能不相交
                return false;
            }
            return GeoUtils.IsPolygonIntersectLine(testPoly.Points, this.Boxstart, this.End)
                || GeoUtils.IsPolygonContainsLine(testPoly.Points, this.Boxstart, this.End);
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            return GeoUtils.IsPolygonContainsLine(testPoly.Points, this.Boxstart, this.End);
        }

        private static List<Line2d> GetShapeCurves(Line2d line, double width)
        {

            double x1 = line.Start.X;
            double y1 = line.Start.Y;
            double x2 = line.End.X;
            double y2 = line.End.Y;
            double setoff = width;
            Vector2 p1 = new Vector2();
            Vector2 p2 = new Vector2();
            Vector2 p3 = new Vector2();
            Vector2 p4 = new Vector2();

            if (x1 == x2)
            {
                p1.Y = y1;
                p1.X = x1 + setoff;
                p2.Y = y1;
                p2.X = x1 - setoff;
                p3.Y = y2;
                p3.X = x2 - setoff;
                p4.Y = y2;
                p4.X = x2 + setoff;
            }
            else
            if (y1 == y2)
            {
                p1.Y = y1 + setoff;
                p1.X = x1;
                p2.Y = y1 - setoff;
                p2.X = x1;
                p3.Y = y2 - setoff;
                p3.X = x2;
                p4.Y = y2 + setoff;
                p4.X = x2;
            }
            else
            {
                double k = (y2 - y1) / (x2 - x1);
                double k1 = -1 / k;
                double b1 = y1 - k1 * x1;



                double xA = x1;
                double yA = y1;
                double xB = x1 + Math.Pow(width, 2);
                double yB = k1 * xB + b1;

                // 给定距离
                double distance = setoff;

                // 计算线段的方向向量
                double directionX = xB - xA;
                double directionY = yB - yA;

                // 计算方向向量的模长
                double length = Math.Sqrt(directionX * directionX + directionY * directionY);

                // 标准化方向向量（单位化）
                double unitDirectionX = directionX / length;
                double unitDirectionY = directionY / length;

                // 计算目标点的坐标
                p1.X = xA + distance * unitDirectionX;
                p1.Y = yA + distance * unitDirectionY;


                Matrix3 matrix3 = Matrix3.GetMove(p1, line.Start);
                p2 = matrix3.MultiplyPoint(line.Start);

                matrix3 = Matrix3.GetMove(line.Start, line.End);

                p3 = matrix3.MultiplyPoint(p2);
                p4 = matrix3.MultiplyPoint(p1);

            }

            List<Line2d> line2Ds = new List<Line2d>();
            Line2d line2D = new Line2d();
            line2D.Start = p1;
            line2D.End = p2;

            line2Ds.Add(line2D);
            line2D = new Line2d();
            line2D.Start = p2;
            line2D.End = p3;

            line2Ds.Add(line2D);
            line2D = new Line2d();
            line2D.Start = p3;
            line2D.End = p4;

            line2Ds.Add(line2D);
            line2D = new Line2d();
            line2D.Start = p4;
            line2D.End = p1;

            line2Ds.Add(line2D);
            return line2Ds;
        }

        public override LcElement ApplyMatrix(Matrix3 matrix3)
        {
            this.Start = matrix3.MultiplyPoint(this.Start);
            this.TextStart = matrix3.MultiplyPoint(this.TextStart);
            return this;
        }
        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            writer.WriteVector2dProperty(nameof(this.Start), this.Start);
            writer.WriteVector2dProperty(nameof(this.TextStart), this.TextStart);
            writer.WriteVector2dProperty(nameof(this.Boxstart), this.Boxstart);
            writer.WriteStringProperty(nameof(this.Text), this.Text);
            writer.WriteStringProperty(nameof(this.Heigh), this.Heigh.ToString());
            writer.WriteStringProperty(nameof(this.Typeface), this.Typeface);
            writer.WriteStringProperty(nameof(this.Tilt), this.Tilt.ToString());
            writer.WriteStringProperty(nameof(this.Widthfactor), this.Widthfactor.ToString());
            writer.WriteStringProperty(nameof(this.Rotate), this.Rotate.ToString());
            writer.WriteStringProperty(nameof(this.Alignment), this.Alignment.ToString());

        }
        public override void ReadProperties(ref JsonElement jele)
        {
            this.Start = jele.ReadVector2dProperty(nameof(Start));
            this.TextStart = jele.ReadVector2dProperty(nameof(TextStart));
            this.Boxstart = jele.ReadVector2dProperty(nameof(Boxstart));
            this.Text = jele.ReadStringProperty(nameof(Text));
            string strHeigh = jele.ReadStringProperty(nameof(Heigh));
            double.TryParse(strHeigh, out this.Heigh);
            this.Typeface = jele.ReadStringProperty(nameof(Typeface));
            string strTilt = jele.ReadStringProperty(nameof(Tilt));
            double.TryParse(strTilt, out this.Tilt);
            string strWidthfactor = jele.ReadStringProperty(nameof(Widthfactor));
            double.TryParse(strWidthfactor, out this.Widthfactor);
            string strRotate = jele.ReadStringProperty(nameof(Rotate));
            double.TryParse(strRotate, out this.Rotate);
            this.Alignment = jele.ReadStringProperty(nameof(Alignment));
        }
    }
}
